const gulp = require('gulp');
// const { src,dest } = require('gulp');
// const uglify = require('gulp-uglify'); //js文件压缩
const dels   = require('del');
const gulpif = require('gulp-if');
const fs = require('fs');
const replace = require('gulp-replace');
var cleanCSS  = require('gulp-clean-css');//压缩CSS为一行；
var rev       = require('gulp-rev');//对文件名加MD5后缀
var revCollector = require('gulp-rev-collector');//路径替换
var htmlmin = require('gulp-htmlmin');//html压缩组件
var distPath  = 'dist/';//构建输出的目录
var prodPath  = 'prod/';//构建输出的目录
var manifest  = 'rev-manifest.json';
var fileCache = {};
var fileCaching = {};
var varFile     = distPath + 'var.json' ;
// var replace = require('gulp-replace');//文件名替换
// var removeEmptyLines = require('gulp-remove-empty-lines');//清除空白行
// var includer = require('gulp-html-ssi'); // bug: 允许嵌套,打包时合并
// var jshint   = require('gulp-jshint');//js语法检查
// var concat   = require('gulp-concat');//多个文件合并为一个
// var gulpRemoveHtml = require('gulp-remove-html');//标签清除
var ssiVars     = {};
var parseVars   = false;

function isJavaScript(file) {
  return file.extname === '.js';
}
function isHtml(file) {
  return file.extname === '.html';
}
function isCss(file) {
  return file.extname === '.css';
}
function del(cb) {
  dels(distPath+'**/*');
  // dels([manifest,distPath+'**/*']);
  return cb();
}
function delProd(cb) {
  dels(prodPath+'**/*');
  return cb();
}

// 处理 dist里的include等
function revProdFile(){
  ssiVars = JSON.parse(fs.readFileSync(varFile,'utf-8'));
  // return gulp.src(['dist/**/*.html','!dist/{lib,api,tpl}/**'
  //   ,'!dist/**/setFrame.html'])
  // return gulp.src(['dist/menu/set.html','dist/static/**'],{base: './dist'})
  return gulp.src(['dist/**/*.html','dist/static/**'],{base: './dist'})
    .pipe(gulpif(isHtml
      ,replace(varReg,ssiVar)
    ))
    // TODO 处理 ssi 的 if 标签  => js 模板
    .pipe(gulpif(isHtml
      ,replace(/([\s\S]+)/,ssiIf)
    ))
    // 不符合html规范,需要先自行去掉注释
    // .pipe(gulpif(isHtml
    //   ,htmlmin({
    //     removeComments: false, // TODO ssi怎么保留:提前处理
    //     collapseWhitespace: false,
    //     minifyJS: false, // TODO 特殊符号:<>也也变了
    //     minifyCSS: false // TODO
    //   })
    // ))
    .pipe(gulp.dest(prodPath));
}
// 处理 include , 保存ssi变量到 var.json
function revFile(){
  return gulp.src(['dist/rev-manifest.json','dist/static/*.{js,css}','src/*.html','src/**/*.html','!src/{api,lib,tpl,static}/**','!src/**/setFrame.html']
      ,{base: './src'})
      .pipe( revCollector({
        replaceReved: true //允许替换, 已经被替换过的文件
        // ,revSuffix: '-[0-9a-f]{8,10}-?'
        // ,dirReplacements: {
        //     'lib': '/lib'
        //     ,'user': '/user'
        //     ,'menu': '/menu'
        //     ,'tpl': '/tpl'
        // ,'cdn/': function(manifest_value) {
        //     return '//cdn' + (Math.floor(Math.random() * 9) + 1) + '.' + 'exsample.dot' + '/img/' + manifest_value;
        // }
        // }
      }))
    .pipe(gulpif(isHtml
      ,replace(includeReg,ssiInclude)
    ))
    .pipe(gulp.dest(distPath));
}
var varDefReg  = /<!--#\s*set\s+var="(\S*)"\s*value="(\S*)"\s*-->/g;
// 仅支持 : <!--# echo var='__CDN__' -->
var varReg     = /<!--#\s*echo\s+var=('\S*'|"\S*")\s*-->/g;
var ssiVar = function(match,p1,offset,string) {
  let pkey = p1.substr(1,p1.length-2);
  // debug('varReplace :' +pkey + ':'+ ssiVars[pkey]);
  return ssiVars[pkey] || '';
}

// 怎么才能匹配到最里面的if
// 仅支持 : <!--# if expr="$auth_type = auth_slide" -->
// <!--# elif expr="$auth_type = auth_slide" -->
// <!--# else -->
// <!--# endif -->
// eval
var ssiIf = function(tpl,p1,offset,string){
  tpl = tpl.replace(/`/g,'\\`');
  var data   = ssiVars;
  var ssiIf  = /<!--#\s*if\s+expr=("|&quot;|')\s*\$(.*)\s+=\s+(\w+)\s*("|&quot;|')\s*-->/g;
  var ssiIf2 = /<!--#\s*if\s+expr=("|&quot;|')\s*\$(.*)\s+(\w+)\s*("|&quot;|')\s*-->/g;
  var ssiIf3 = /<!--#\s*if\s+expr=("|&quot;|')\s*\$(.*)\s*("|&quot;|')\s*-->/g;
  var ssiElseif  = /<!--#\s*elif\s+expr=("|&quot;|')\s*\$(.*)\s+=\s+(\w+)\s*("|&quot;|')\s*-->/g;
  var ssiElseif2 = /<!--#\s*elif\s+expr=("|&quot;|')\s*\$(.*)\s+(\w+)\s*("|&quot;|')\s*-->/g;
  var ssiElseif3 = /<!--#\s*elif\s+expr=("|&quot;|')\s*\$(.*)\s*("|&quot;|')\s*-->/g;
  var ssiElse  = /<!--#\s*else\s*-->/g;
  var ssiEnd   = /<!--#\s*endif\s*-->/g;
  // // var expr     = /{{([\s\S]+?)}}/g;
  tpl = tpl.replace(ssiIf, '`);if( data.$2=="$3" ) { \n echo(`')
        .replace(ssiIf2, '`);if( data.$2"$3" ) { \n echo(`')
        .replace(ssiIf3, '`);if( data.$2 ) { \n echo(`')
        .replace(ssiElseif, '`); \n } else if( data.$2=="$3" ) { \n  echo(`')
        .replace(ssiElseif2, '`); \n } else if( data.$2"$3" ) { \n  echo(`')
        .replace(ssiElseif3, '`); \n } else if( data.$2 ) { \n  echo(`')
        .replace(ssiElse, '`); \n }else{  echo(`')
        .replace(ssiEnd, '`); \n  } echo(`')
        ;
    // .replace(expr, '`); \n $1 \n  echo(`');
  // tpl = 'var $auth_silde="auth_slide";echo(`' + tpl + '`);';
  tpl = 'echo(`' + tpl + '`);';
  // debug(tpl);
  // return tpl;
  // console.log('tpl',tpl);
  var script = `(function parse(data){
    let output = "";
    function echo(html){
      output += html;
    }
    ${ tpl }
    return output;
  })`;
  // debug(script);
  tpl = eval(script)(data);
  // 去掉js注释 合并换行空格
  tpl = tpl.replace(/<js>([\s\S]*)<\/js>/,function(m){
    return removeJsMark(m);
  }).replace(/<script(.*?)>([\s\S]*?)<\/script>/g,function(m){
    return removeJsMark(m);
  });
  // 去掉html注释 合并换行空格
  tpl = tpl.replace(/<html(.*?)>([\s\S]*)<\/html>/,function(m){
      return removeHtmlMark(m)
  }).replace(/<tpl>([\s\S]*)<\/tpl>/,function(m){
      return removeHtmlMark(m)
  });
  return tpl;
}
function removeHtmlMark(str) {
  return str.replace(/<!--([\s\S]*?)-->/g, "").replace(/\s+/g, ' ');
}
function removeJsMark(tpl){
  reg = /("([^\\\"]*(\\.)?)*")|('([^\\\']*(\\.)?)*')|(\/{2,}.*?(\r|\n|$))|(\/\*(\n|.)*?\*\/)/g;
  return tpl.replace(reg, function(word) {
      // 去除注释后的文本
    return /^\/{2,}/.test(word) || /^\/\*/.test(word) ? "" : word;
  }).replace(/\s+/g, ' ');
}
function debug(s,m='') {
  let err = s;
  if(typeof s == 'object') {
    err = JSON.stringify(s);
  }
  fs.writeFileSync('log'+(m ? '_'+m : '')+'.txt', err + '\r\n');
  // throw new Error((m ? m +' : ' : '') + err);
}

var includeReg = /<!--#\s*include\s+(virtual|file)="(\S*)".*-->/g;
var ssiInclude = function(match,p1,p2,offset,string) {
  // if(fileCaching[p4]) throw new Error('循环依赖: ' + p4 + '\n' + match);
  if(match) {
    let str;
    // match : <!--# include ... -->
    // string: the whole string
    if(p1=='virtual') { // ssi: include virtual
      fileCaching[p2] = 1;
      if(p2 == '/lib/var.html') {
        if(!parseVars){
          let tmp = includeCache(p2);
          if(varDefReg.test(tmp)) {
            tmp.replace(varDefReg,function(match2,p21,p22,offset2,string2) {
              ssiVars[p21] = p22;
            });
          }
          // debug(ssiVars);
          parseVars = true;
          // 写入var.json文件
          fs.writeFileSync(varFile,JSON.stringify(ssiVars),{ encoding: 'utf8', mode: 438 /*=0666*/, flag: 'w' });
        }
        return '';
      }
      str = includeIn(p2);
      return includeReg.test(str) ? str.replace(includeReg,ssiInclude) : str;
    }else{ // ssi: include file
      return match;
    }
  }else{ // 完结
    fileCaching = [];
    return string;
  }
}
function includeCache(p) {
  if(fileCache[p]){
    return fileCache[p];
  }else{
    fileCache[p] = fs.readFileSync('./src' + p, 'utf8');
    return fileCache[p];
  }
}
function includeIn(p) {
  return includeCache(p);
}
// TODO 压缩时怎么保留SSI语句
function jscss(){
  // https://github.com/mishoo/UglifyJS#minify-options-structure
  // mangle: true,//类型：Boolean 默认：true 是否修改变量名
  // compress: true,//类型：Boolean 默认：true 是否完全压缩
  // preserveComments: 'all' //保留所有注释，该参数还有其它的值，如license、function、some
  // output: { comments: false } //去除所有注释
  // mangle: {reserved: ['require' ,'exports' ,'module' ,'$']}//排除混淆关键字
  return gulp.src(['src/static/**/**'], {base: './src'})
    // .pipe(gulpif(isJavaScript,uglify({
    //   // compress: false,
    //   mangle: { reserved: ['require' ,'exports' ,'module' ,'$', 'layui','window'] },
    //   output: { comments: true }
    //   // output: { comments: function(node, comment) {
    //   //   return /include/.test(comment.value);
    //   // }}
    // })))
    .pipe(gulpif(isCss,cleanCSS()))
    .pipe(rev())
    .pipe(gulp.dest(distPath))
    .pipe(rev.manifest({
      path: manifest,
      merge: true
    }))
    .pipe(gulp.dest(distPath));
}

// gulp3 语法
// gulp.task('default', ['del','jscss','revFile']);
exports.del     = del;
exports.jscss   = jscss;
exports.revFile = revFile;
exports.delProd     = delProd;
exports.revProdFile = revProdFile;
// 1,删除 dist*
// 2,处理 css,js文件 + MD5
// 3,处理 html,js,css文件 + 根据manifest替换资源
// 解析
exports.dist = gulp.series(del, jscss, revFile);
// 发布
exports.prod = gulp.series(delProd, revProdFile);
// exports.default = gulp.series(del, gulp.parallel(js, css), revFile);
// TODO watch
// exports.watch = function (){
//     gulp.watch('src/**.html', ['revFile']);
//     gulp.watch('src/**/*.{js,css}', ['default']);
// }
// gulp dist
// gulp prod